// 2025/4/8: 首个记录
// 2025/6/12: 协议名常量改为public

package com.appox.samples;

import com.appox.Sample;
import com.appox.GeneralSample;
import com.appox.GeneralElement;
import java.util.ArrayList;
import java.util.List;

/**
 * 超声波传感器样本数据类
 */
public class USoundSample extends Sample {
    
    /**
     * 超声波传感器属性类
     */
    public static class USoundSensorProperty {
        private double originX; // [m] 传感器原点X坐标
        private double originY; // [m] 传感器原点Y坐标
        private double orientation; // [°] 传感器中心轴朝向
        private double angleRangeAlpha; // [°] α角范围（一般在100°左右，以OriginX/Y为圆心）
        private double angleRangeBeta; // [°] β角范围（一般在0~30°）
        private double distanceRangeAlpha; // [m] α角对应的探测距离（较短）
        private double distanceRangeBeta; // [m] β角对应的探测距离（较长）
        
        /**
         * 构造函数，设置默认属性值
         */
        public USoundSensorProperty() {
            angleRangeAlpha = 100;
            angleRangeBeta = 15;
            distanceRangeAlpha = 0.5;
            distanceRangeBeta = 2.5;
        }
        
        public double getOriginX() {
            return originX;
        }
        
        public void setOriginX(double originX) {
            this.originX = originX;
        }
        
        public double getOriginY() {
            return originY;
        }
        
        public void setOriginY(double originY) {
            this.originY = originY;
        }
        
        public double getOrientation() {
            return orientation;
        }
        
        public void setOrientation(double orientation) {
            this.orientation = orientation;
        }
        
        public double getAngleRangeAlpha() {
            return angleRangeAlpha;
        }
        
        public void setAngleRangeAlpha(double angleRangeAlpha) {
            this.angleRangeAlpha = angleRangeAlpha;
        }
        
        public double getAngleRangeBeta() {
            return angleRangeBeta;
        }
        
        public void setAngleRangeBeta(double angleRangeBeta) {
            this.angleRangeBeta = angleRangeBeta;
        }
        
        public double getDistanceRangeAlpha() {
            return distanceRangeAlpha;
        }
        
        public void setDistanceRangeAlpha(double distanceRangeAlpha) {
            this.distanceRangeAlpha = distanceRangeAlpha;
        }
        
        public double getDistanceRangeBeta() {
            return distanceRangeBeta;
        }
        
        public void setDistanceRangeBeta(double distanceRangeBeta) {
            this.distanceRangeBeta = distanceRangeBeta;
        }
    }
    
    /**
     * 超声波传感器数据类
     */
    public static class USoundSensorData {
        private int id; // 传感器ID，从1开始
        private USoundSensorProperty properties; // 传感器属性
        private Double distance; // [m] 障碍物的距离，无障碍物则为null
        
        /**
         * 构造函数
         */
        public USoundSensorData() {
            properties = new USoundSensorProperty();
        }
        
        public int getId() {
            return id;
        }
        
        public void setId(int id) {
            this.id = id;
        }
        
        public USoundSensorProperty getProperties() {
            return properties;
        }
        
        public void setProperties(USoundSensorProperty properties) {
            this.properties = properties;
        }
        
        public Double getDistance() {
            return distance;
        }
        
        public void setDistance(Double distance) {
            this.distance = distance;
        }
    }
    
    private List<USoundSensorData> dataList; // 各个超声波传感器的数据
    
    /**
     * 构造函数
     */
    public USoundSample() {
        dataList = new ArrayList<>();
    }
    
    /**
     * 获取传感器数据列表
     */
    public List<USoundSensorData> getDataList() {
        return dataList;
    }
    
    /**
     * 设置传感器数据列表
     */
    public void setDataList(List<USoundSensorData> dataList) {
        this.dataList = dataList;
    }
    
    /**
     * 获取通用样本协议
     */
    @Override
    public String getGeneralSampleProtocol() {
        return TARGET_PROTOCOL;
    }
    
    /**
     * 获取通用样本协议列表
     */
    @Override
    public String[] getGeneralSampleProtocols() {
        return PROTOCOLS;
    }
    
    /**
     * 将超声波传感器样本转换为通用样本
     */
    @Override
    public GeneralSample toGeneralSample(Integer channel) {
        GeneralSample sample = new GeneralSample();
        sample.setTime(this);
        sample.setProtocol(TARGET_PROTOCOL);
        sample.setChannel(channel);
        
        GeneralElement[] data = new GeneralElement[1 + 9 * dataList.size()];
        
        // 初始化所有元素
        for (int i = 0; i < data.length; i++) {
            data[i] = new GeneralElement();
        }
        
        data[0] = new GeneralElement((double)dataList.size());
        
        int baseIndex = 1;
        for (int i = 0; i < dataList.size(); i++) {
            USoundSensorData sensor = dataList.get(i);
            data[baseIndex + 0] = new GeneralElement((double)sensor.getId());
            if (sensor.getDistance() != null) data[baseIndex + 1] = new GeneralElement(sensor.getDistance());
            data[baseIndex + 2] = new GeneralElement(sensor.getProperties().getOriginX());
            data[baseIndex + 3] = new GeneralElement(sensor.getProperties().getOriginY());
            data[baseIndex + 4] = new GeneralElement(sensor.getProperties().getOrientation());
            data[baseIndex + 5] = new GeneralElement(sensor.getProperties().getAngleRangeAlpha());
            data[baseIndex + 6] = new GeneralElement(sensor.getProperties().getAngleRangeBeta());
            data[baseIndex + 7] = new GeneralElement(sensor.getProperties().getDistanceRangeAlpha());
            data[baseIndex + 8] = new GeneralElement(sensor.getProperties().getDistanceRangeBeta());
            
            baseIndex += 9;
        }
        
        sample.setValues(data);
        sample.setNumberOfSignificants(data.length);
        
        return sample;
    }
    
    /**
     * 从通用样本转换为超声波传感器样本
     */
    @Override
    public boolean fromGeneralSample(GeneralSample sample) {
        setTime(sample);
        
        if (TARGET_PROTOCOL.equals(sample.getProtocol())) {
            return fromUSoundSampleV1(sample);
        }
        
        return false;
    }
    
    /**
     * 从USoundSampleV1通用样本转换
     */
    private boolean fromUSoundSampleV1(GeneralSample sample) {
        GeneralElement[] v = sample.getValues();
        if (v.length < 1) return false;
        if (!isNumber(v[0])) return false;
        
        int sensorCount = (int)v[0].getValue();
        if (v.length != 1 + sensorCount * 9) return false;
        
        dataList = new ArrayList<>();
        for (int i = 0; i < sensorCount; i++) {
            USoundSensorData data = new USoundSensorData();
            data.setId(toInt(v[1 + 9 * i]));
            data.setDistance(toDouble(v[2 + 9 * i]));
            data.getProperties().setOriginX(toDouble(v[3 + 9 * i]));
            data.getProperties().setOriginY(toDouble(v[4 + 9 * i]));
            data.getProperties().setOrientation(toDouble(v[5 + 9 * i]));
            data.getProperties().setAngleRangeAlpha(toDouble(v[6 + 9 * i]));
            data.getProperties().setAngleRangeBeta(toDouble(v[7 + 9 * i]));
            data.getProperties().setDistanceRangeAlpha(toDouble(v[8 + 9 * i]));
            data.getProperties().setDistanceRangeBeta(toDouble(v[9 + 9 * i]));
            dataList.add(data);
        }
        
        return true;
    }
    
    /**
     * 是否支持插值
     */
    @Override
    public boolean supportInterpolation() {
        return true;
    }
    
    /**
     * 对两个超声波传感器样本进行插值
     */
    @Override
    public Sample interpolate(SearchResult result) {
        USoundSample s1 = (USoundSample)result.getS1();
        USoundSample s2 = (USoundSample)result.getS2();
        double w1 = result.getW1();
        double w2 = result.getW2();
        
        USoundSample output = new USoundSample();
        
        // 遍历第一个样本中的所有传感器数据
        for (USoundSensorData sensor1 : s1.getDataList()) {
            // 在第二个样本中查找相同ID的传感器
            for (USoundSensorData sensor2 : s2.getDataList()) {
                // 如果找到相同ID的传感器，则执行插值
                if (sensor1.getId() == sensor2.getId()) {
                    USoundSensorData interpolatedSensor = new USoundSensorData();
                    interpolatedSensor.setId(sensor1.getId());
                    
                    // 对于Properties，直接使用第一个样本中的属性
                    // 因为传感器的物理属性通常不会改变
                    USoundSensorProperty props = new USoundSensorProperty();
                    props.setOriginX(sensor1.getProperties().getOriginX());
                    props.setOriginY(sensor1.getProperties().getOriginY());
                    props.setOrientation(sensor1.getProperties().getOrientation());
                    props.setAngleRangeAlpha(sensor1.getProperties().getAngleRangeAlpha());
                    props.setAngleRangeBeta(sensor1.getProperties().getAngleRangeBeta());
                    props.setDistanceRangeAlpha(sensor1.getProperties().getDistanceRangeAlpha());
                    props.setDistanceRangeBeta(sensor1.getProperties().getDistanceRangeBeta());
                    interpolatedSensor.setProperties(props);
                    
                    // 对于Distance，只有当两个样本中都有值时才进行插值
                    // 否则设为null
                    if (sensor1.getDistance() != null && sensor2.getDistance() != null) {
                        interpolatedSensor.setDistance(sensor1.getDistance() * w1 + sensor2.getDistance() * w2);
                    } else {
                        interpolatedSensor.setDistance(null);
                    }
                    
                    output.getDataList().add(interpolatedSensor);
                    break;
                }
            }
        }
        
        return output;
    }
    
    /**
     * 检查元素是否是数字
     */
    private boolean isNumber(GeneralElement element) {
        return element != null && element.isValid() && !element.isText();
    }
    
    /**
     * 获取整数值
     */
    private int toInt(GeneralElement element) {
        if (element == null || !isNumber(element)) {
            return 0;
        }
        return (int)element.getValue();
    }
    
    /**
     * 获取Double值，如果元素不是数字则返回null
     */
    private Double toDouble(GeneralElement element) {
        if (element == null || !isNumber(element)) {
            return null;
        }
        return element.getValue();
    }

    // 协议相关常量
    public static final String TARGET_PROTOCOL = "usound-sample-v1";
    public static final String[] PROTOCOLS = { "usound-sample-v1" };
} 